home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 5
/
MacMania 5.toast
/
/
Internet software
/
NewsWatcher
/
NW Source
/
Source
/
full.c
< prev
next >
Wrap
Text File
|
1997-01-09
|
29KB
|
1,000 lines
/*----------------------------------------------------------------------------
full.c
This module handles tasks involving the full group list.
Copyright © 1994-1997, Northwestern University.
----------------------------------------------------------------------------*/
#include <string.h>
#include <stdio.h>
#include "glob.h"
#include "full.h"
#include "qsort.h"
#include "dialog.h"
#include "news.h"
#include "newswatcher.h"
#include "menus.h"
#include "status.h"
#include "wind.h"
#include "group.h"
#include "text.h"
#include "memutil.h"
#include "strutil.h"
#include "windutil.h"
#include "subscribe.h"
#include "fileutil.h"
#include "biglist.h"
#define kMustCloseBeforeRebuildDlg 150
static Handle gSortGroupNames; /* handle to group name strings during sorting */
/*----------------------------------------------------------------------------
FindGroupIndex
Find the index of a group in gGroupNameOffsets.
Entry: name = group name.
gFullGroupNameOffsets sorted in increasing order by group name.
Exit: function result = index in gGroupNameOffsets of group,
or -1 if not found.
----------------------------------------------------------------------------*/
long FindGroupIndex (char *name)
{
long low = 0;
long high = gNumGroups-1;
long mid;
short compare;
long nameOffset;
while (low <= high) {
mid = (low + high) >> 1;
nameOffset = (*gGroupNameOffsets)[mid] & 0x7fffffff; /* mask off sign bit
which might be set by DoCheckForDeletedGroups - see below. */
compare = strcmp(name, *gGroupNames + nameOffset);
if (compare == 0) {
return mid;
} else if (compare < 0) {
high = mid-1;
} else {
low = mid+1;
}
}
return -1;
}
/*----------------------------------------------------------------------------
FindGroupOffset
Find the offset of a group name in gGroupNames.
Entry: name = group name.
gFullGroupNameOffsets sorted in increasing order by group name.
Exit: function result = offset in gGroupNames of group, or -1 if not found.
----------------------------------------------------------------------------*/
long FindGroupOffset (char *name)
{
long index;
index = FindGroupIndex(name);
return index == -1 ? -1 : (*gGroupNameOffsets)[index];
}
/*----------------------------------------------------------------------------
GroupCompare
The group comparison routine used in the calls to FastQSort in
SortGroupNameOffsets and CheckForNewGroups below. It does a
simple string compare and gives time to background applications.
Entry: p = pointer to first group name offset.
q = pointer to second group name offset.
gSortGroupNames = handle to group names block.
Exit: function result = error code.
*result
< 0 if first group name < second group name.
= 0 if first group name = second group name.
> 0 if first group name > second group name.
----------------------------------------------------------------------------*/
static OSErr GroupCompare (long *p, long *q, short *result)
{
OSErr err;
static short counter = 0;
if ((++counter & 0x1f) == 0) {
err = GiveTime(false);
if (err != noErr) return err;
counter = 0;
}
*result = strcmp(*gSortGroupNames + *p, *gSortGroupNames + *q);
return noErr;
}
/*----------------------------------------------------------------------------
SortFullGroupList
Sort a full group list.
Entry: groupNameOffsets = handle to array of group name offsets.
numGroups = number of groups in array.
groupNames = handle to group names block.
Exit: function result = error code.
groupNameOffsets array sorted into increasing order by group name.
----------------------------------------------------------------------------*/
static OSErr SortFullGroupList (long **groupNameOffsets, long numGroups, Handle groupNames)
{
OSErr err = noErr;
char state;
if (numGroups > 0) {
err = DisplayStatusMessageNumber(kStrSortingStatusMsg);
if (err != noErr) return err;
gSortGroupNames = groupNames;
state = MyHGetState(groupNameOffsets);
MyHLock(groupNameOffsets);
err = FastQSort(*groupNameOffsets, numGroups, sizeof(long),
(SortCmpFunction)GroupCompare);
MyHSetState(groupNameOffsets, state);
if (err != noErr) return err;
}
return noErr;
}
/*----------------------------------------------------------------------------
ReadGroupsFromPrefs
Read the full group list stored on the preferences file.
Entry: fSpec = pointer to file spec of prefs file.
prefsInDataFork = true if prefs are in data fork of prefs file
and the full group list follows the prefs. False if the
prefs are in the resource fork and the data fork contains
only the full group list.
prefsVersion = version number of prefs file.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr ReadGroupsFromPrefs (FSSpec *fSpec, Boolean prefsInDataFork,
unsigned long prefsVersion)
{
OSErr err = noErr;
short fRefNum = 0;
Boolean needSort = false;
char *p, *pEnd;
char *prevName = nil;
char *curName;
long *x;
long groupNamesSize, i;
char state;
err = DisplayStatusMessageNumber(kStrReadingStatusMsg);
if (err != noErr) goto exit;
err = FSpOpenDF(fSpec, fsRdPerm, &fRefNum);
if (err != noErr) return noErr;
/* Read the saved group names. */
err = GetEOF(fRefNum, &groupNamesSize);
if (err != noErr) goto exit;
if (prefsInDataFork) {
groupNamesSize -= sizeof(TPrefRec);
if (groupNamesSize < 0) {
err = eofErr;
goto exit;
}
err = SetFPos(fRefNum, fsFromStart, sizeof(TPrefRec));
if (err != noErr) goto exit;
}
err = MyNewHandle(groupNamesSize, &gGroupNames);
if (err != noErr) goto exit;
state = MyHGetState(gGroupNames);
MyHLock(gGroupNames);
err = FSRead(fRefNum, &groupNamesSize, *gGroupNames);
MyHSetState(gGroupNames, state);
if (err != noErr) goto exit;
MyFSClose(fRefNum, nil);
fRefNum = 0;
/* Special case the Cornell version 2.0d15-CU, which put
a 32 byte authorization username and 32 bytes of zero at
the end of the NU 2.0d14 prefs, which in any NU version
of NW shows up as the first 64 bytes of what we think
is the full group list! (Yes, yuck).
First we check prefsVersion to see if it is 2.0d14. If
it is, we then check byte 32 of the full group list. If
byte 32 is 0, we assume this is a Cornell prefs file. In
this case, we copy the first 32 bytes (the Cornell authorization
username) to gPrefs.authUsername (our authorization username).
We discard the next unused 32 zero bytes.
Note that in NU prefs files, a 0 byte never appears in the
full group list, so theoretically there is no chance of
confusing an NU prefs file with a CU prefs file.
This makes NU NewsWatcher 2.0d27 and later understand and
properly convert 2.0d15-CU prefs files. The reverse will never
work - 2.0d15-CU is not able to make any sense of any NU version
prefs file.
*/
if (prefsVersion == 0x02002014 && groupNamesSize >= 64 &&
*(*gGroupNames+32) == 0)
{
BlockMoveData(*gGroupNames, gPrefs.authUsername, 32);
groupNamesSize -= 64;
BlockMoveData(*gGroupNames + 64, *gGroupNames, groupNamesSize);
MySetHandleSize(gGroupNames, groupNamesSize);
gFullGroupListDirty = true;
}
/* Check to make certain the last group name ends in CR. If not, strip
any trailing junk. */
p = *gGroupNames + groupNamesSize - 1;
while (p >= *gGroupNames && *p != CR) p--;
p++;
if (p < *gGroupNames + groupNamesSize) {
groupNamesSize = p - *gGroupNames;
MySetHandleSize(gGroupNames, groupNamesSize);
gFullGroupListDirty = true;
if (p == *gGroupNames) return noErr;
}
/* Walk through the gGroupNames buffer. Count the number of groups.
Change all CR to 0. Check to see if the groups are already sorted
(they should be). */
p = *gGroupNames;
pEnd = p + groupNamesSize;
gNumGroups = 0;
while (p < pEnd) {
curName = p;
while (*p != CR) p++;
*p++ = 0;
gNumGroups++;
needSort = needSort || (prevName != nil && strcmp(prevName, curName) > 0);
prevName = curName;
}
/* Allocate and initialize the group name offsets array. */
err = MyNewHandle(gNumGroups*sizeof(long), &gGroupNameOffsets);
if (err != noErr) goto exit;
for (i = 0, x = *gGroupNameOffsets, p = *gGroupNames; i < gNumGroups; i++, x++) {
*x = p - *gGroupNames;
p += strlen(p) + 1;
}
/* If necessary, sort the group names offset array. */
if (needSort) {
gFullGroupListDirty = true;
err = SortFullGroupList(gGroupNameOffsets, gNumGroups, gGroupNames);
if (err != noErr) goto exit;
}
return noErr;
exit:
MyDisposeHandle(gGroupNames);
if (fRefNum != 0) MyFSClose(fRefNum, nil);
gNumGroups = 0;
return err;
}
/*----------------------------------------------------------------------------
AdjustFullGroupListChildWindows
This function must be called whenever the full group list changes. It locates
all the open child subject list windows. If a group has been deleted, any
associated open child subject list window is closed. If a group still exists,
the "parentGroup" backpointer in the child window's TWindow info is adjusted
to point to the new location of the parent group in the group name offsets array
gGroupNameOffsets.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr AdjustFullGroupListChildWindows (void)
{
TWindow **info, **childInfo;
TChild **childList, **prevChildList;
WindowPtr childWindow;
CStr255 groupName;
long index;
OSErr err = noErr;
gFullGroupListDirty = true;
if (gFullGroupWindow == nil) return noErr;
info = (TWindow**)GetWRefCon(gFullGroupWindow);
childList = (**info).childList;
prevChildList = nil;
while (childList != nil) {
childWindow = (**childList).childWindow;
childInfo = (TWindow**)GetWRefCon(childWindow);
strcpy(groupName, *gGroupNames + (**childInfo).groupNameOffset);
index = FindGroupIndex(groupName);
if (index == -1) {
/* Group has been deleted. Close the child window. */
err = DoClose(childWindow);
if (err != noErr) return err;
if (prevChildList == nil) {
childList = (**info).childList;
} else {
childList = (**prevChildList).next;
}
} else {
/* Group still exists. Update the parentGroup backpointer. */
(**childInfo).parentGroup = index;
prevChildList = childList;
childList = (**childList).next;
}
}
return noErr;
}
/*----------------------------------------------------------------------------
UpdateFullGroupWindow
Make sure that the Full Group List window corresponds to the changed
full group list. It must be called whenever the full group list changes.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr UpdateFullGroupWindow (void)
{
TWindow **info;
GrafPtr port;
OSErr err = noErr;
GetPort(&port);
err = AdjustFullGroupListChildWindows();
if (err != noErr) return err;
if (gFullGroupWindow != nil) {
info = (TWindow**)GetWRefCon(gFullGroupWindow);
(**info).groupNameOffsets = gGroupNameOffsets;
(**info).numGroups = gNumGroups;
err = InitializeGroupList(gNumGroups, (**info).groupList, kFullGroup);
if (err != noErr) return err;
BigLSelectOnlyOne((**info).groupList, 0);
SetPort(gFullGroupWindow);
InvalRect(&gFullGroupWindow->portRect);
}
SetPort(port);
return noErr;
}
/*----------------------------------------------------------------------------
MergeNewGroupsIntoFullGroupList
Merge new groups into the full group list. Both lists must be sorted on
entry. The full group list remains sorted on exit.
Entry: newGroupNameOffsets = handle to new groups array of group
name offsets.
numNew = number of new groups.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr MergeNewGroupsIntoFullGroupList (long **newGroupNameOffsets, long numNew)
{
long numLeftToInsert, numToMoveUp;
long *fullListPtr, *newListPtr;
char *newName;
OSErr err = noErr;
gNumGroups += numNew;
err = MySetHandleSize(gGroupNameOffsets, gNumGroups*sizeof(long));
if (err != noErr) return err;
numLeftToInsert = numNew;
fullListPtr = *gGroupNameOffsets + gNumGroups - numNew - 1;
newListPtr = *newGroupNameOffsets + numNew - 1;
while (numLeftToInsert > 0) {
newName = *gGroupNames + *newListPtr;
numToMoveUp = 0;
while (fullListPtr >= *gGroupNameOffsets &&
strcmp(newName, *gGroupNames + *fullListPtr) <= 0)
{
fullListPtr--;
numToMoveUp++;
}
if (numToMoveUp > 0)
BlockMoveData(fullListPtr + 1, fullListPtr + numLeftToInsert + 1,
numToMoveUp*sizeof(long));
BlockMoveData(newListPtr, fullListPtr + numLeftToInsert, sizeof(long));
newListPtr--;
numLeftToInsert--;
}
return noErr;
}
/*----------------------------------------------------------------------------
CheckForNewGroups
Check for any new groups created since the last time we checked.
Exit: function result = error code.
*newGroupNameOffsets = handle to array of group name offsets for the
new groups, or nil if numNewGroups == 0.
*numNewGroups = number of new groups.
----------------------------------------------------------------------------*/
OSErr CheckForNewGroups (long ***newGroupNameOffsets, long *numNewGroups)
{
OSErr err = noErr;
short len, nameWidth;
long numGroups, numNew, i;
Handle strings = nil;
char *p, *q;
long offset, savedGroupNamesSize;
long **newOffsets = nil;
long *x;
CStr255 groupName;
GrafPtr port;
char state;
WindowPtr wind;
TWindow **info;
Handle unsubscribed;
GetPort(&port);
savedGroupNamesSize = GetHandleSize(gGroupNames);
err = DisplayStatusMessageNumber(kStrCheckingNewStatusMsg);
if (err != noErr) goto exit;
/* Get the new group names from the server. */
err = GetGroupNames(gPrefs.groupCheckTime, &strings, &numGroups);
if (err != noErr) goto exit;
/* Check for duplicates. Filter out the groups which are already present in
the full group list. */
numNew = 0;
for (i = 0, p = *strings, q = *strings; i < numGroups; i++) {
len = strlen(q);
if (FindGroupIndex(q) == -1) {
strcpy(p, q);
p += len+1;
numNew++;
}
q += len+1;
}
MySetHandleSize(strings, p - *strings);
if (numNew == 0) {
MyDisposeHandle(strings);
*numNewGroups = 0;
*newGroupNameOffsets = nil;
GetDateTime(&gPrefs.groupCheckTime);
SetPort(port);
return noErr;
}
/* Append the new group names to the end of gGroupNames. */
offset = savedGroupNamesSize;
err = MyHandAndHand(strings, gGroupNames);
if (err != noErr) goto exit;
/* Allocate and initialize the array of offsets for the new groups. */
err = MyNewHandle(numNew * sizeof(long), &newOffsets);
if (err != noErr) goto exit;
for (i = 0, x = *newOffsets, p = *gGroupNames + offset; i < numNew; i++, x++) {
*x = p - *gGroupNames;
p += strlen(p) + 1;
}
/* Sort the new offsets array. */
gSortGroupNames = gGroupNames;
state = MyHGetState(newOffsets);
MyHLock(newOffsets);
err = FastQSort(*newOffsets, numNew, sizeof(long), (SortCmpFunction)GroupCompare);
MyHSetState(newOffsets, state);
if (err != noErr) goto exit;
/* Merge the new groups into the full group list. */
err = MergeNewGroupsIntoFullGroupList(newOffsets, numNew);
if (err != noErr) goto exit;
err = UpdateFullGroupWindow();
if (err != noErr) goto exit;
/* Update the last new groups check date and time. */
GetDateTime(&gPrefs.groupCheckTime);
/* Check to see if one of the new group names is now the widest group
name in the full group list window. */
SetPort(gFullGroupWindow);
if (gPrefs.maxGroupNameWidth > 0) {
for (i = 0; i < numNew; i++) {
strcpy(groupName, *gGroupNames + (*newOffsets)[i]);
nameWidth = TextWidth(groupName, 0, strlen(groupName));
if (nameWidth > gPrefs.maxGroupNameWidth) gPrefs.maxGroupNameWidth = nameWidth;
}
}
/* Add each new group name to the unsubscribed list of each open user group
list window. */
wind = FrontWindow();
while (wind != nil) {
if (GetMyWindowKind(wind) == kGroup) {
info = (TWindow**)GetWRefCon(wind);
unsubscribed = (**info).unsubscribed;
if (unsubscribed != nil) {
for (i = 0; i < numNew; i++) {
strcpy(groupName, *gGroupNames + (*newOffsets)[i]);
err = AddGroupToUnsubscribedList(groupName, unsubscribed);
if (err != noErr) goto exit;
}
}
}
wind = (WindowPtr)((WindowPeek)wind)->nextWindow;
}
/* Return. */
*newGroupNameOffsets = newOffsets;
*numNewGroups = numNew;
SetPort(port);
return noErr;
exit:
MyDisposeHandle(strings);
MyDisposeHandle(newOffsets);
MySetHandleSize(gGroupNames, savedGroupNamesSize);
SetPort(port);
return err;
}
/*----------------------------------------------------------------------------
DoCheckForNewGroups
Handle the "Check for New Groups" command.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr DoCheckForNewGroups (void)
{
long **newOffsets;
long numGroups;
WindowPtr wind;
OSErr err = noErr;
err = CheckForNewGroups(&newOffsets, &numGroups);
if (err != noErr) return err;
if (numGroups > 0) {
return MakeNewGroupsWindow(newOffsets, numGroups, &wind);
} else {
return noErr;
}
}
/*----------------------------------------------------------------------------
DoCheckForDeletedGroups
Handle the "Check for Deleted Groups" command.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr DoCheckForDeletedGroups (void)
{
Handle strings = nil;
Handle deleted = nil;
long deletedNext, deletedAllocated, offset;
long numDel, numToMoveDown;
short len, nameWidth;
long numGroups, i, index, nameOffset;
long *x, *prev, *cur, *curEnd;
char *p;
OSErr err = noErr;
CStr255 groupName;
GrafPtr port;
Str255 title;
WindowPtr wind;
long groupNamesSize, memNeeded;
Handle tempGroupNames = nil;
Boolean fullGroupListMovedToTempMem = false;
Boolean savedCriticalSeq;
GetPort(&port);
err = DisplayStatusMessageNumber(kStrCheckingDelStatusMsg);
if (err != noErr) goto exit;
/* Check available memory. If we don't have at least 1.25 times the size
of the current full group list available in the application heap,
try to move the current full group list into temporary memory to
make room to get the new full group list. */
if (gGroupNames != nil) {
groupNamesSize = MyGetHandleSize(gGroupNames);
memNeeded = groupNamesSize;
memNeeded += memNeeded >> 2;
if (!MemoryAvailable(memNeeded)) {
if (HaveModernTempMemory()) {
err = MyTempNewHandle(groupNamesSize, &tempGroupNames);
if (err != noErr) goto exit;
BlockMoveData(*gGroupNames, *tempGroupNames, groupNamesSize);
MyDisposeHandle(gGroupNames);
gGroupNames = tempGroupNames;
fullGroupListMovedToTempMem = true;
} else {
return memFullErr;
}
}
}
/* Get a list of all group names from the server. */
err = GetGroupNames(0, &strings, &numGroups);
if (err != noErr) goto exit;
/* Mark all the groups in the full group list by setting the sign bit
in each element of the group name offsets array. Then walk the
fresh full group list we just got from the server and clear the
sign bit for each of the groups which still exist. This leaves just
the deleted groups marked with the sign bit set. */
for (i = 0, x = *gGroupNameOffsets; i < gNumGroups; i++, x++) *x |= 0x80000000;
for (i = 0, offset = 0; i < numGroups; i++) {
err = GiveTime(false);
if (err != noErr) goto exit;
p = *strings + offset;
index = FindGroupIndex(p);
if (index != -1) (*gGroupNameOffsets)[index] &= 0x7fffffff;
offset += strlen(p) + 1;
}
MyDisposeHandle(strings);
strings = nil;
/* If we moved the full group list to temp mem, copy it back to real mem. */
if (fullGroupListMovedToTempMem) {
BeginCriticalMemorySequence(&savedCriticalSeq);
MyNewHandle(groupNamesSize, &gGroupNames);
BlockMoveData(*tempGroupNames, *gGroupNames, groupNamesSize);
MyDisposeHandle(tempGroupNames);
EndCriticalMemorySequence(savedCriticalSeq);
fullGroupListMovedToTempMem = false;
}
/* Allocate a buffer to hold the names of the deleted groups for display
to the user. */
err = MyNewHandle(0, &deleted);
if (err != noErr) goto exit;
deletedNext = deletedAllocated = 0;
/* Walk the full group list. Copy the names of the deleted groups to the
buffer. Also check to see if the group with the widest name in the full
group list window has been deleted. */
SetPort(gFullGroupWindow);
numDel = 0;
for (i = 0; i < gNumGroups; i++) {
x = *gGroupNameOffsets + i;
if (*x < 0) {
nameOffset = *x & 0x7fffffff;
err = GiveTime(false);
if (err != noErr) goto exit;
numDel++;
strcpy(groupName, *gGroupNames + nameOffset);
len = strlen(groupName);
if (deletedNext + len + 1 > deletedAllocated) {
deletedAllocated += 1000;
err = MySetHandleSize(deleted, deletedAllocated);
if (err != noErr) goto exit;
}
strcpy(*deleted + deletedNext, groupName);
deletedNext += len+1;
*(*deleted + deletedNext - 1) = CR;
if (gPrefs.maxGroupNameWidth > 0) {
nameWidth = TextWidth(groupName, 0, len);
if (nameWidth >= gPrefs.maxGroupNameWidth) gPrefs.maxGroupNameWidth = 0;
}
}
}
/* If there aren't any deleted groups, issue a note message and return. */
if (numDel == 0) {
MyDisposeHandle(deleted);
NoteMessageNumber(kStrNoDelGroups);
return noErr;
}
MySetHandleSize(deleted, deletedNext);
/* Remove the deleted groups from the full group list. */
prev = *gGroupNameOffsets;
cur = *gGroupNameOffsets;
curEnd = cur + gNumGroups;
numDel = 0;
while (cur < curEnd) {
numToMoveDown = 0;
while (cur < curEnd && *cur >= 0) {
cur++;
numToMoveDown++;
}
if (numDel > 0 && numToMoveDown > 0)
BlockMoveData(prev + numDel, prev, numToMoveDown*sizeof(long));
prev += numToMoveDown;
if (cur < curEnd) numDel++;
cur++;
}
/* Update the full group list window, display the deleted group names to the
user in a text window, and return. */
gNumGroups -= numDel;
MySetHandleSize(gGroupNameOffsets, gNumGroups*sizeof(long));
err = UpdateFullGroupWindow();
if (err != noErr) goto exit;
GetPString(kStrDelGroupsWindTitle, title);
err = MakeNewTextWindow(title, 0, nil, deleted, &wind);
if (err != noErr) goto exit;
MyDisposeHandle(deleted);
SetPort(port);
return noErr;
exit:
MyDisposeHandle(strings);
MyDisposeHandle(deleted);
SetPort(port);
for (i = 0, x = *gGroupNameOffsets; i < gNumGroups; i++, x++) *x &= 0x7fffffff;
if (fullGroupListMovedToTempMem) {
BeginCriticalMemorySequence(&savedCriticalSeq);
MyNewHandle(groupNamesSize, &gGroupNames);
BlockMoveData(*tempGroupNames, *gGroupNames, groupNamesSize);
MyDisposeHandle(tempGroupNames);
tempGroupNames = nil;
EndCriticalMemorySequence(savedCriticalSeq);
}
return err;
}
/*----------------------------------------------------------------------------
MustCloseBeforeRebuildDialog
Present the "must close windows before rebuilding full group list" dialog.
Exit: function result = error code.
----------------------------------------------------------------------------*/
static OSErr MustCloseBeforeRebuildDialog (void)
{
OSErr err = noErr;
DialogPtr dlg = nil;
short item;
err = MyGetNewDialog(kMustCloseBeforeRebuildDlg, ok, cancel, &dlg);
if (err != noErr) return err;
SysBeep(0);
MyModalDialog(dlg, gDialogFilterUPP, &item);
err = DoClose(dlg);
if (err != noErr) return err;
if (item == cancel) return userCanceledErr;
return noErr;
}
/*----------------------------------------------------------------------------
DoRebuildFullGroupList
Handle the "Rebuild Full Group List" command.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr DoRebuildFullGroupList (void)
{
Handle newGroupNames = nil;
long **newGroupNameOffsets = nil;
long newNumGroups;
long *x;
long i;
char *p;
OSErr err = noErr;
WindowPtr wind;
TWindowKind kind;
Boolean promptBeforeClose = true;
long groupNamesSize, groupNameOffsetsSize, memNeeded;
Handle tempGroupNames = nil;
long **tempGroupNameOffsets = nil;
Boolean fullGroupListMovedToTempMem = false;
TWindow **info;
Boolean savedCriticalSeq;
/* Close all group windows except for the full group list window, and close
all subject windows. We must do this because group and subject window data
structures contain offsets into the old group names gGroupNames, which is
about to be blown away and replaced. */
while (true) {
wind = FrontWindow();
while (wind != nil) {
kind = GetMyWindowKind(wind);
if ((kind == kGroup && wind != gFullGroupWindow) ||
kind == kSubject) break;
wind = (WindowPtr)((WindowPeek)wind)->nextWindow;
}
if (wind == nil) break;
if (promptBeforeClose) {
err = MustCloseBeforeRebuildDialog();
if (err != noErr) goto exit;
promptBeforeClose = false;
}
err = DoClose(wind);
if (err != noErr) goto exit;
}
/* Display the status message. */
err = DisplayStatusMessageNumber(kStrGetFullStatusMsg);
if (err != noErr) goto exit;
/* Check available memory. If we don't have at least 1.25 times the size
of the current full group list available in the application heap,
try to move the current full group list into temporary memory to
make room to get the new full group list. */
if (gGroupNames != nil && gGroupNameOffsets != nil) {
groupNamesSize = MyGetHandleSize(gGroupNames);
groupNameOffsetsSize = MyGetHandleSize(gGroupNameOffsets);
memNeeded = groupNamesSize + groupNameOffsetsSize;
memNeeded += memNeeded >> 2;
if (!MemoryAvailable(memNeeded)) {
if (HaveModernTempMemory()) {
err = MyTempNewHandle(groupNamesSize, &tempGroupNames);
if (err != noErr) goto exit;
err = MyTempNewHandle(groupNameOffsetsSize, &tempGroupNameOffsets);
if (err != noErr) goto exit;
BlockMoveData(*gGroupNames, *tempGroupNames, groupNamesSize);
MyDisposeHandle(gGroupNames);
gGroupNames = tempGroupNames;
BlockMoveData(*gGroupNameOffsets, *tempGroupNameOffsets, groupNameOffsetsSize);
MyDisposeHandle(gGroupNameOffsets);
gGroupNameOffsets = tempGroupNameOffsets;
if (gFullGroupWindow != nil) {
info = (TWindow**)GetWRefCon(gFullGroupWindow);
(**info).groupNameOffsets = gGroupNameOffsets;
(**info).numGroups = gNumGroups;
}
fullGroupListMovedToTempMem = true;
} else {
return memFullErr;
}
}
}
/* Get a list of all group names from the server. */
err = GetGroupNames(0, &newGroupNames, &newNumGroups);
if (err != noErr) goto exit;
/* Allocate the new array of group name offsets. */
err = MyNewHandle(newNumGroups * sizeof(long), &newGroupNameOffsets);
if (err != noErr) goto exit;
/* Initialize the new array of group name offsets. */
for (i = 0, x = *newGroupNameOffsets, p = *newGroupNames; i < newNumGroups; i++, x++) {
*x = p - *newGroupNames;
p += strlen(p) + 1;
}
/* Sort the new array of group name offsets. */
err = SortFullGroupList(newGroupNameOffsets, newNumGroups, newGroupNames);
if (err != noErr) goto exit;
/* Make the new array of group name offsets and group names the real ones. Dispose the old
ones. */
MyDisposeHandle(gGroupNameOffsets);
gGroupNameOffsets = newGroupNameOffsets;
newGroupNameOffsets = nil;
gNumGroups = newNumGroups;
MyDisposeHandle(gGroupNames);
gGroupNames = newGroupNames;
newGroupNames = nil;
fullGroupListMovedToTempMem = false;
/* Update the full group list window and return. */
gPrefs.maxGroupNameWidth = 0;
err = UpdateFullGroupWindow();
if (err != noErr) goto exit;
GetDateTime(&gPrefs.groupCheckTime);
return noErr;
exit:
MyDisposeHandle(newGroupNameOffsets);
MyDisposeHandle(newGroupNames);
if (fullGroupListMovedToTempMem) {
BeginCriticalMemorySequence(&savedCriticalSeq);
MyNewHandle(groupNamesSize, &gGroupNames);
BlockMoveData(*tempGroupNames, *gGroupNames, groupNamesSize);
MyNewHandle(groupNameOffsetsSize, &gGroupNameOffsets);
BlockMoveData(*tempGroupNameOffsets, *gGroupNameOffsets, groupNameOffsetsSize);
(**info).groupNameOffsets = gGroupNameOffsets;
(**info).numGroups = gNumGroups;
EndCriticalMemorySequence(savedCriticalSeq);
}
MyDisposeHandle(tempGroupNames);
MyDisposeHandle(tempGroupNameOffsets);
return err;
}